﻿using System;
using System.Collections.Generic;

namespace LxCFD
{
    public class MGZoneCreator
    {
        //Flag----
        protected readonly int Flag_InNone_Inner = -3;
        protected readonly int Flag_InNone_Bound = -4;
        protected readonly int Flag_InGroup = -2;
        protected readonly int Flag_InTempCell = -1;
        protected bool Flag_IsInCreatedCells(int newID)
        {
            return newID >= 0;
        }
        //Flag----end
        public int TargetCoarseNum;
        public int[] InCoarseCell;
        public int[] GroupCells;
        public QueueTool QueueTool_O;
        public QueueTool QueueTool_N;
        int editPos;
        List<int> tempMGCell = new List<int>();


        IProvideZoneTopology finezone;
        MGZone coarseZone;
        public List<MGCellTemp> coarseCells;
        public List<MGFaceTemp> coarseFaces;
        public MGZone CreateMGZone(IProvideZoneTopology finezone)
        {
            this.finezone = finezone;
            coarseZone = new MGZone();

            initialize();
            createInnerMGCells();
            createInnerCoarseFaces();
            for (int i1 = 0; i1 < finezone.GetThreadCount(); i1++)
            {
                createBoundCoarseFacesAndCells(finezone.GetThreadIndex(i1));
            }
            addCoarseFaceAndCellToCoarseZone();
            checkTopology();
            coarseCells.Clear();
            coarseFaces.Clear();
            return coarseZone;
        }
        #region 内部粗网格划分         
        private void initialize()
        {
            int totalCell = finezone.GetTotalIndex().CellIDs.GetCount();
            if (InCoarseCell.Length < totalCell)
            {
                InCoarseCell = new int[totalCell];
            }
            IndexRecorder cellIDs = finezone.GetInnerIndex().CellIDs;
            for (int i1 = cellIDs.Start; i1 <= cellIDs.End; i1++)
            {
                InCoarseCell[i1] = Flag_InNone_Inner;//内部初始
            }
            for (int i1 = 0; i1 < finezone.GetThreadCount(); i1++)
            {
                cellIDs = finezone.GetThreadIndex(i1).CellIDs;
                for (int i2 = cellIDs.Start; i2 <= cellIDs.End; i2++)
                {
                    InCoarseCell[i2] = Flag_InNone_Bound;//边界初始
                }
            }
        }
        private void createInnerMGCells()
        {
            IndexRecorder cellIDs = finezone.GetInnerIndex().CellIDs;
            int zoneStartId = finezone.GetThreadIndex(0).FaceIDs.Start;
            zoneStartId = finezone.GetFaceOSideCell(zoneStartId);
            editPos = 0;
            for (int i1 = zoneStartId; i1 <= cellIDs.End; i1++)
            {
                createGroupAndSplitCell(i1);
            }
            for (int i1 = cellIDs.Start; i1 < zoneStartId; i1++)
            {
                createGroupAndSplitCell(i1);
            }
        }
        void createGroupAndSplitCell(int startCell)
        {
            if (!tryCreateGroup(startCell))
                return;
            int searchPos = 0;
            int searchEndp1 = editPos;
            while (searchPos < searchEndp1)
            {
                for (; searchPos < searchEndp1; ++searchPos)
                {
                    foreach (int adjFace in finezone.GetCellOFaces(GroupCells[searchPos]))
                    {
                        int adjcell = finezone.GetFaceNSideCell(adjFace);
                        if (tryCreateGroup(adjcell) && editPos == GroupCells.Length)
                        {
                            splitGroup();
                            return;
                        }
                    }
                    foreach (int adjFace in finezone.GetCellNFaces(GroupCells[searchPos]))
                    {
                        int adjcell = finezone.GetFaceOSideCell(adjFace);
                        if (tryCreateGroup(adjcell) && editPos == GroupCells.Length)
                        {
                            splitGroup();
                            return;
                        }
                    }

                }
                searchEndp1 = editPos;
            }
            splitGroup();
        }
        bool tryCreateGroup(int cellIndex)
        {
            if (InCoarseCell[cellIndex] == Flag_InNone_Inner)
            {
                InCoarseCell[cellIndex] = Flag_InGroup;
                GroupCells[editPos] = cellIndex;
                ++editPos;
                return true;
            }
            else
            {
                return false;
            }
        }
        void splitGroup()
        {
            editPos--;
            for (; editPos >= 0; editPos--)
            {
                splitMGCell(GroupCells[editPos]);
            }
            editPos = 0;
        }
        private void splitMGCell(int startCell)
        {
            if (!trySplitMGCell(startCell))
                return;
            int searchPos = 0;
            int searchEndp1 = tempMGCell.Count;
            while (searchPos < searchEndp1)
            {
                for (; searchPos < searchEndp1; ++searchPos)
                {
                    foreach (int adjFace in finezone.GetCellOFaces(tempMGCell[searchPos]))
                    {
                        int adjcell = finezone.GetFaceNSideCell(adjFace);
                        if (trySplitMGCell(adjcell) && tempMGCell.Count >= TargetCoarseNum)
                        {
                            addTempCellToList();
                            return;
                        }
                    }
                    foreach (int adjFace in finezone.GetCellNFaces(tempMGCell[searchPos]))
                    {
                        int adjcell = finezone.GetFaceOSideCell(adjFace);
                        if (trySplitMGCell(adjcell) && tempMGCell.Count >= TargetCoarseNum)
                        {
                            addTempCellToList();
                            return;
                        }
                    }
                }
                searchEndp1 = tempMGCell.Count;
            }
            if (tempMGCell.Count > 0.5 * TargetCoarseNum)
            {
                addTempCellToList();
            }
            else
            {
                int adjCoarsecell = 0;
                int adjCoarsecellfinecount = int.MaxValue;
                foreach (int fc in tempMGCell)
                {
                    foreach (int adjFace in finezone.GetCellOFaces(fc))
                    {
                        int adjcell = finezone.GetFaceNSideCell(adjFace);
                        int newID = InCoarseCell[adjcell];
                        if (Flag_IsInCreatedCells(newID))//已创建的网格，其Index范围为0~CoarseCell.Count-1
                        {
                            int fineCellsCount = coarseCells[newID].FineCells.Count;
                            if (fineCellsCount < adjCoarsecellfinecount)
                            {
                                adjCoarsecellfinecount = fineCellsCount;
                                adjCoarsecell = newID;
                            }
                        }
                    }
                    foreach (int adjFace in finezone.GetCellNFaces(fc))
                    {
                        int adjcell = finezone.GetFaceOSideCell(adjFace);
                        int newID = InCoarseCell[adjcell];
                        if (Flag_IsInCreatedCells(newID))//已创建的网格，其Index范围为0~CoarseCell.Count-1
                        {
                            int fineCellsCount = coarseCells[newID].FineCells.Count;
                            if (fineCellsCount < adjCoarsecellfinecount)
                            {
                                adjCoarsecellfinecount = fineCellsCount;
                                adjCoarsecell = newID;
                            }
                        }
                    }
                }
                if (adjCoarsecellfinecount == int.MaxValue)
                {
                    addTempCellToList();
                }
                else
                {
                    foreach (var fc in tempMGCell)
                    {
                        InCoarseCell[fc] = adjCoarsecell;
                    }
                    coarseCells[adjCoarsecell].FineCells.AddRange(tempMGCell);
                    tempMGCell = new List<int>();
                }
            }
        }
        void addTempCellToList()
        {
            foreach (var fc in tempMGCell)
            {
                InCoarseCell[fc] = coarseCells.Count;
            }
            coarseCells.Add(new MGCellTemp() { FineCells = tempMGCell });
            tempMGCell = new List<int>();
        }
        bool trySplitMGCell(int adjcell)
        {
            if (InCoarseCell[adjcell] == Flag_InGroup)
            {
                tempMGCell.Add(adjcell);
                InCoarseCell[adjcell] = Flag_InTempCell;
                return true;
            }
            return false;
        }
        #endregion
        private void createInnerCoarseFaces()
        {
            QueueTool_N.Create(coarseCells.Count, finezone.GetInnerIndex().FaceIDs.GetCount());
            IndexRecorder faceIDs = finezone.GetInnerIndex().FaceIDs;
            for (int i1 = faceIDs.Start; i1 <= faceIDs.End; i1++)
            {
                ONPair onfinemesh = finezone.GetONCellID(i1);
                ONPair onnew = new ONPair(
                    InCoarseCell[onfinemesh.Owner],
                    InCoarseCell[onfinemesh.Neighbor]);
                if (onnew.Owner == onnew.Neighbor)
                {
                    QueueTool_N.AddOneToQueue(onnew.Owner, i1);
                    //coarseCells[onnew.Owner].InnerFineFaces.Add(i1);
                }
                else
                {
                    AddFace(i1, onnew);
                }
            }
            splitCoarseFacesFromCellStorageToList();
            coarseZone.InnerIndex = new IndexNumber(0, coarseCells.Count - 1, 0, coarseFaces.Count - 1);
        }
        void AddFace(int i1, ONPair onnew)
        {
            if (onnew.Owner < onnew.Neighbor)//默认Face存储到小编号的粗网格中
            {
                foreach (MGFaceTemp ccf in coarseCells[onnew.Owner].CoarseOFacesStorage)
                {
                    if (ccf.NeighborCellIndex == onnew.Neighbor)
                    {
                        ccf.FineFace_O2N.Add(i1);
                        return;
                    }
                }
                MGFaceTemp ccfnew = new MGFaceTemp()
                {
                    OwnerCellIndex = onnew.Owner,
                    NeighborCellIndex = onnew.Neighbor,
                };
                ccfnew.FineFace_O2N.Add(i1);
                coarseCells[onnew.Owner].CoarseOFacesStorage.Add(ccfnew);
            }
            else
            {
                foreach (MGFaceTemp ccf in coarseCells[onnew.Neighbor].CoarseOFacesStorage)
                {
                    if (ccf.NeighborCellIndex == onnew.Owner)
                    {
                        ccf.FineFace_N2O.Add(i1);
                        return;
                    }
                }
                MGFaceTemp ccfnew = new MGFaceTemp()
                {
                    OwnerCellIndex = onnew.Neighbor,
                    NeighborCellIndex = onnew.Owner,
                };
                ccfnew.FineFace_N2O.Add(i1);
                coarseCells[onnew.Neighbor].CoarseOFacesStorage.Add(ccfnew);
            }
        }
        private void createBoundCoarseFacesAndCells(IndexNumber fineThreadIndex)
        {
            //先创建face，face暂时存储在临时数据
            int startCellId = coarseCells.Count, startFaceId = coarseFaces.Count;
            IndexRecorder faceIDs = fineThreadIndex.FaceIDs;
            for (int i1 = faceIDs.Start; i1 <= faceIDs.End; ++i1)
            {
                int fineO = finezone.GetFaceOSideCell(i1);
                int coarseOwner = InCoarseCell[fineO];
                ref List<MGFaceTemp> storage = ref coarseCells[coarseOwner].CoarseOFacesStorage;
                if (storage.Count == 0)
                {
                    MGFaceTemp coarsefacetemp = new MGFaceTemp()
                    {
                        OwnerCellIndex = coarseOwner,
                    };
                    coarsefacetemp.FineFace_O2N.Add(i1);
                    storage.Add(coarsefacetemp);
                }
                else
                {
                    storage[0].FineFace_O2N.Add(i1);
                }
            }
            //后从coarsecell中找到刚刚添加的面，转移到coarseFace中
            splitCoarseFacesFromCellStorageToList();
            //最后补充coarseCell
            for (int i1 = startFaceId; i1 < coarseFaces.Count; ++i1)
            {
                coarseFaces[i1].NeighborCellIndex = coarseCells.Count;
                //创建粗Cell
                MGCellTemp threadcoarsecell = new MGCellTemp();
                foreach (var f in coarseFaces[i1].FineFace_O2N)
                {
                    threadcoarsecell.FineCells.Add(finezone.GetFaceNSideCell(f));
                }
                coarseCells.Add(threadcoarsecell);
            }
            IndexNumber coarseIndex = new IndexNumber(startCellId, coarseCells.Count - 1, startFaceId, coarseFaces.Count - 1);
            coarseZone.ThreadIndexs.Add(coarseIndex);
        }
        private void splitCoarseFacesFromCellStorageToList()
        {
            foreach (MGCellTemp coarsecell in this.coarseCells)
            {
                if (coarsecell.CoarseOFacesStorage.Count != 0)
                {
                    coarseFaces.AddRange(coarsecell.CoarseOFacesStorage);
                    coarsecell.CoarseOFacesStorage = new List<MGFaceTemp>();
                }
            }
        }
        private void addCoarseFaceAndCellToCoarseZone()
        {
            //添加face信息
            MGFace[] structFaces = new MGFace[coarseFaces.Count];
            for (int i1 = 0; i1 < structFaces.Length; i1++)
            {
                structFaces[i1] = coarseFaces[i1].ConverterToCoarseFace();
            }
            coarseZone.Faces = structFaces;
            //添加cell信息
            MGCell[] structCells = new MGCell[this.coarseCells.Count];
            for (int i1 = 0; i1 < structCells.Length; i1++)
            {
                structCells[i1].FineCells = coarseCells[i1].FineCells.ToArray();
                //structCells[i1].InnerFineFaces = coarseCells[i1].InnerFineFaces.ToArray();
            }
            //先把原先已添加的innerFace加上
            IndexRecorder innerCellIDs = coarseZone.InnerIndex.CellIDs;
            for (int i1 = innerCellIDs.Start; i1 <= innerCellIDs.End; ++i1)
            {
                this.QueueTool_N.ConvertQueueToArray(out structCells[i1].InnerFineFaces, i1);
            }
            for (int i1 = innerCellIDs.End + 1; i1 < structCells.Length; ++i1)
            {
                structCells[i1].InnerFineFaces = new int[0];
            }
            QueueTool_N.Reset();
            QueueTool_O.Create(structCells.Length, structFaces.Length);
            QueueTool_N.Create(structCells.Length, structFaces.Length);
            for (int i1 = 0; i1 < structFaces.Length; ++i1)
            {
                QueueTool_O.AddOneToQueue(structFaces[i1].OSideCell, i1);
                QueueTool_N.AddOneToQueue(structFaces[i1].NSideCell, i1);
            }
            for (int i1 = 0; i1 < structCells.Length; ++i1)
            {
                QueueTool_O.ConvertQueueToArray(out structCells[i1].OFaces, i1);
                QueueTool_N.ConvertQueueToArray(out structCells[i1].NFaces, i1);
            }
            coarseZone.Cells = structCells;
            coarseZone.TotalIndex = new IndexNumber(0, coarseZone.Cells.Length - 1, 0, coarseZone.Faces.Length - 1);
        }
        public void checkTopology()
        {
            foreach (var coarseIndex in coarseZone.ThreadIndexs)
            {
                for (int i1 = coarseIndex.CellIDs.Start; i1 <= coarseIndex.CellIDs.End; i1++)
                {
                    foreach (var fc in coarseCells[i1].FineCells)
                    {
                        InCoarseCell[fc] = i1;
                    }
                }
            }
            IndexNumber FineInnerIndex = finezone.GetInnerIndex();
            int fineFaceCountInFineZone = 0, fineCellCountInFineZone = 0;
            fineCellCountInFineZone += FineInnerIndex.CellIDs.GetCount();
            fineFaceCountInFineZone += FineInnerIndex.FaceIDs.GetCount();
            for (int i1 = 0; i1 < finezone.GetThreadCount(); i1++)
            {
                IndexNumber ind = finezone.GetThreadIndex(i1);
                fineCellCountInFineZone += ind.CellIDs.GetCount();// ind.CellIDs.End - ind.CellIDs.Start + 1;
                fineFaceCountInFineZone += ind.FaceIDs.GetCount();// ind.FaceIDs.End - ind.FaceIDs.Start + 1;
            }
            int fineCellCountInCoarse = 0;
            for (int i1 = 0; i1 < coarseZone.Cells.Length; i1++)
            {
                for (int i2 = 0; i2 < coarseZone.Cells[i1].FineCells.Length; i2++)
                {
                    fineCellCountInCoarse++;
                    if (InCoarseCell[coarseZone.Cells[i1].FineCells[i2]] != i1)
                        Console.WriteLine("AMG error in coarse {0} in fine {1}", i1, coarseZone.Cells[i1].FineCells[i2]);
                }
            }
            if (fineCellCountInCoarse != fineCellCountInFineZone)
            {

                Console.WriteLine("AMG error finecellcount inIndex:{0} inCoarse:{1}", fineCellCountInFineZone, fineCellCountInCoarse);
            }
            for (int i1 = 0; i1 < coarseZone.Cells.Length; i1++)//检查粗网格面与网格对应关系
            {
                for (int i2 = 0; i2 < coarseZone.Cells[i1].OFaces.Length; i2++)
                {
                    if (coarseZone.Faces[coarseZone.Cells[i1].OFaces[i2]].OSideCell != i1)
                        Console.WriteLine("AMG error in coarseface");
                }
                for (int i2 = 0; i2 < coarseZone.Cells[i1].NFaces.Length; i2++)
                {
                    if (coarseZone.Faces[coarseZone.Cells[i1].NFaces[i2]].NSideCell != i1)
                        Console.WriteLine("AMG error in coarseface");
                }
            }
            int fineFaceCountInCoarse = 0;
            for (int i1 = 0; i1 < coarseZone.Cells.Length; i1++)//检查粗网格内部面
            {
                foreach (var ii in coarseZone.Cells[i1].InnerFineFaces)
                {
                    fineFaceCountInCoarse++;
                    ONPair on = finezone.GetONCellID(ii);
                    if (InCoarseCell[on.Owner] != i1 || InCoarseCell[on.Neighbor] != i1)
                        Console.WriteLine("AMG error in face");
                }
            }
            for (int i1 = 0; i1 < coarseZone.Faces.Length; i1++)//检查粗网格交界面
            {
                for (int i2 = 0; i2 < coarseZone.Faces[i1].FineFaces_O2N.Length; i2++)
                {
                    fineFaceCountInCoarse++;
                    ONPair on = finezone.GetONCellID(coarseZone.Faces[i1].FineFaces_O2N[i2]);
                    if (InCoarseCell[on.Owner] != coarseZone.Faces[i1].OSideCell || InCoarseCell[on.Neighbor] != coarseZone.Faces[i1].NSideCell)
                        Console.WriteLine("AMG error in face");
                }
                for (int i2 = 0; i2 < coarseZone.Faces[i1].FineFaces_N2O.Length; i2++)
                {
                    fineFaceCountInCoarse++;
                    ONPair on = finezone.GetONCellID(coarseZone.Faces[i1].FineFaces_N2O[i2]);
                    if (InCoarseCell[on.Owner] != coarseZone.Faces[i1].NSideCell || InCoarseCell[on.Neighbor] != coarseZone.Faces[i1].OSideCell)
                        Console.WriteLine("AMG error in face");
                }
            }
            if (fineFaceCountInCoarse != fineFaceCountInFineZone)
            {
                Console.WriteLine("AMG error finefacecount inIndex:{0} inCoarse:{1}", fineFaceCountInFineZone, fineFaceCountInCoarse);
            }
        }

    }
}
